using System.Collections.Concurrent;
using System.Collections.Generic;

namespace OracleInternal.Common
{
	internal class CacheWithLRUList<keyType, valType>
	{
		private object m_LRUsync = new object();

		private ConcurrentDictionary<keyType, CachedLRUItem<valType, LinkedListNode<keyType>>> m_cache;

		private LinkedList<keyType> m_LRUList;

		internal int m_maxCacheSize;

		internal int Count
		{
			get
			{
				if (m_cache != null)
				{
					return m_cache.Count;
				}
				return 0;
			}
		}

		internal CacheWithLRUList(int maxCacheSize)
		{
			m_maxCacheSize = maxCacheSize;
			m_cache = new ConcurrentDictionary<keyType, CachedLRUItem<valType, LinkedListNode<keyType>>>();
			m_LRUList = new LinkedList<keyType>();
		}

		internal virtual valType RemoveLRU(int count = 1)
		{
			CachedLRUItem<valType, LinkedListNode<keyType>> value = null;
			lock (m_LRUsync)
			{
				while (m_LRUList.Count > 0 && count > 0)
				{
					if (m_cache.TryRemove(m_LRUList.First!.Value, out value))
					{
						m_LRUList.Remove(m_LRUList.First);
					}
					count--;
				}
			}
			if (value != null)
			{
				return value.m_value;
			}
			return default(valType);
		}

		internal virtual valType Put(keyType key, valType value, bool updateLRU = false)
		{
			valType val = default(valType);
			LinkedListNode<keyType> linkedListNode = new LinkedListNode<keyType>(key);
			CachedLRUItem<valType, LinkedListNode<keyType>> value2 = new CachedLRUItem<valType, LinkedListNode<keyType>>(value, linkedListNode);
			bool flag = m_cache.TryAdd(key, value2);
			if (!updateLRU)
			{
				return value;
			}
			if (flag)
			{
				if (m_cache.Count > m_maxCacheSize)
				{
					val = RemoveLRU();
				}
				lock (m_LRUsync)
				{
					m_LRUList.AddLast(linkedListNode);
				}
			}
			if (val == null)
			{
				return default(valType);
			}
			return val;
		}

		internal virtual valType Get(keyType key)
		{
			CachedLRUItem<valType, LinkedListNode<keyType>> value = null;
			m_cache.TryGetValue(key, out value);
			if (value != null)
			{
				return value.m_value;
			}
			return default(valType);
		}

		internal virtual bool ContainsKey(keyType key)
		{
			return m_cache.ContainsKey(key);
		}

		internal void Purge()
		{
			if (m_LRUList.Count <= 0)
			{
				lock (m_LRUsync)
				{
					m_cache.Clear();
				}
				return;
			}
			CachedLRUItem<valType, LinkedListNode<keyType>> value = null;
			lock (m_LRUsync)
			{
				while (m_LRUList.Count > 0)
				{
					if (m_cache.TryRemove(m_LRUList.First!.Value, out value))
					{
						m_LRUList.Remove(m_LRUList.First);
					}
				}
			}
		}
	}
}
